package java.util;

/**
 * @author RAMESH
 *
 */

import com.google.gwt.core.client.JsDate;
import com.google.gwt.user.client.Window;

import java.io.Console;
import java.io.Serializable;

import com.client.*;

/**
 * Represents a date and time.
 */
public class Date implements Cloneable, Comparable<Date>, Serializable {

  /**
   * Encapsulates static data to avoid Date itself having a static initializer.
   */
  private static class StringData {
    public static final String[] DAYS = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public static final String[] MONTHS = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
        "Nov", "Dec"};
  }

  public static long parse(String s) {
    double parsed = JsDate.parse(s);
    if (Double.isNaN(parsed)) {
      throw new IllegalArgumentException();
    }
    return (long) parsed;
  }

  // CHECKSTYLE_OFF: Matching the spec.
  public static long UTC(int year, int month, int date, int hrs, int min,
      int sec) {
    return (long) JsDate.UTC(year + 1900, month, date, hrs, min, sec, 0);
  }

  // CHECKSTYLE_ON

  /**
   * Ensure a number is displayed with two digits.
   * 
   * @return a two-character base 10 representation of the number
   */
  protected static String padTwo(int number) {
    if (number < 10) {
      return "0" + number;
    } else {
      return String.valueOf(number);
    }
  }

  /**
   * Package private factory for JSNI use, to allow cheap creation of dates from
   * doubles.
   */
  static Date createFrom(double milliseconds) {
    return new Date(milliseconds, false);
  }

  /**
   * JavaScript Date instance.
   */
  private final JsDate jsdate;

  public Date() {
    jsdate = JsDate.create();
    
    applyTimeZone();
  }

  public void applyTimeZone() {
//    Window.alert("milli sec before converstion:"+jsdate.getTime());
	    
	    //needs to initiate the time for org timezone. 
	    TimeZone timeZone = TimeZone.getTimeZone(TestTimeZone.myTimeZone);
	    
	    int offsetConversion = jsdate.getTimezoneOffset() 
	    						- timeZone.timeZoneContainer.getTimeZone().getStandardOffset();
	    
//	    Window.alert("date default timezone offset:" + jsdate.getTimezoneOffset());
//	    Window.alert("timezone offset:" + timeZone.timeZoneContainer.getTimeZone().getStandardOffset());
//	    Window.alert("timezone daylight adjustment offset:" + timeZone.timeZoneContainer.getTimeZone().getDaylightAdjustment(jsdate));
	//    
	    if (offsetConversion != 0) {
//	    	Window.alert("time before conversion:"+jsdate.getTime());
	    	
	    	jsdate.setMinutes(jsdate.getMinutes() + offsetConversion);

//	    	Window.alert("time after conversion:"+jsdate.getTime());
	    	
//	    	jsdate.setTime( jsdate.getTime() + offsetConversion*60*1000);
	    	
//	    Window.alert("date before daylight adjustment:" + jsdate.toString());
	    
	    	jsdate.setMinutes(jsdate.getMinutes() + timeZone.timeZoneContainer.getTimeZone().getDaylightAdjustment(jsdate));
	    }
//	    Window.alert("milli sec after converstion:"+jsdate.getTime());
//	    Window.alert("date after daylight adjustment:" + jsdate.toString());	  
  }
  
  public int getServerConversionOffset() {
	  
	    //needs to initiate the time for org timezone. 
	    TimeZone timeZone = TimeZone.getTimeZone(TestTimeZone.myTimeZone);
	    
	    int offsetConversion = -jsdate.getTimezoneOffset() 
	    						+ timeZone.timeZoneContainer.getTimeZone().getStandardOffset();
	  

	    if (offsetConversion == 0)
	    	return offsetConversion;	    
	    
	    return  offsetConversion - timeZone.timeZoneContainer.getTimeZone().getDaylightAdjustment(jsdate);	    
  }
  
  public Date(int year, int month, int date) {
    this(year, month, date, 0, 0, 0);
  }

  public Date(int year, int month, int date, int hrs, int min) {
    this(year, month, date, hrs, min, 0);
  }

  public Date(int year, int month, int date, int hrs, int min, int sec) {
    jsdate = JsDate.create();
    jsdate.setFullYear(year + 1900, month, date);
    jsdate.setHours(hrs, min, sec, 0);
    fixDaylightSavings(hrs);
    
  }

  public Date(long date) {
    jsdate = JsDate.create(date);
    
//    applyTimeZone();
  }
  

  public Date(String date) {
    this(Date.parse(date));
  }

  /**
   * For use by {@link #createFrom(double)}, should inline away.
   */
  Date(double milliseconds, boolean dummyArgForOverloadResolution) {
    jsdate = JsDate.create(milliseconds);
  }

  public boolean after(Date when) {
    return getTime() > when.getTime();
  }

  public boolean before(Date when) {
    return getTime() < when.getTime();
  }

  public Object clone() {
    return new Date(getTime());
  }

  public int compareTo(Date other) {
    return Long.signum(getTime() - other.getTime());
  }

  @Override
  public boolean equals(Object obj) {
    return ((obj instanceof Date) && (getTime() == ((Date) obj).getTime()));
  }

  public int getDate() {
    return jsdate.getDate();
  }

  public int getDay() {
    return jsdate.getDay();
  }

  public int getHours() {
    return jsdate.getHours();
  }

  public int getMinutes() {
    return jsdate.getMinutes();
  }

  public int getMonth() {
    return jsdate.getMonth();
  }

  public int getSeconds() {
    return jsdate.getSeconds();
  }

  public long getTime() {
    return (long) jsdate.getTime();
  }

  public int getTimezoneOffset() {
    //return jsdate.getTimezoneOffset();

	    //needs to initiate the time for org timezone. 
	    TimeZone timeZone = TimeZone.getTimeZone(TestTimeZone.myTimeZone);

	 return timeZone.timeZoneContainer.getTimeZone().getStandardOffset() - timeZone.timeZoneContainer.getTimeZone().getDaylightAdjustment(jsdate); 
  }

  public int getYear() {
    return jsdate.getFullYear() - 1900;
  }

  @Override
  public int hashCode() {
    long time = getTime();
    return (int) (time ^ (time >>> 32));
  }

  public void setDate(int date) {
    int hours = jsdate.getHours();
    jsdate.setDate(date);
    fixDaylightSavings(hours);
  }

  public void setHours(int hours) {
    jsdate.setHours(hours);
    fixDaylightSavings(hours);
  }

  public void setMinutes(int minutes) {
    int hours = getHours() + minutes / 60;
    jsdate.setMinutes(minutes);
    fixDaylightSavings(hours);
  }

  public void setMonth(int month) {
    int hours = jsdate.getHours();
    jsdate.setMonth(month);
    fixDaylightSavings(hours);
  }

  public void setSeconds(int seconds) {
    int hours = getHours() + seconds / (60 * 60);
    jsdate.setSeconds(seconds);
    fixDaylightSavings(hours);
  }

  public void setTime(long time) {
    jsdate.setTime(time);
  }

  public void setYear(int year) {
    int hours = jsdate.getHours();
    jsdate.setFullYear(year + 1900);
    fixDaylightSavings(hours);
  }

  public String toGMTString() {
    return jsdate.getUTCDate() + " " + StringData.MONTHS[jsdate.getUTCMonth()]
        + " " + jsdate.getUTCFullYear() + " " + padTwo(jsdate.getUTCHours())
        + ":" + padTwo(jsdate.getUTCMinutes()) + ":"
        + padTwo(jsdate.getUTCSeconds()) + " GMT";
  }

  public String toLocaleString() {
    return jsdate.toLocaleString();
  }

  @Override
  public String toString() {
    // Compute timezone offset. The value that getTimezoneOffset returns is
    // backwards for the transformation that we want.

	  //get timezone offset based on the defined one
    TimeZone timeZone = TimeZone.getTimeZone(TestTimeZone.myTimeZone);
	  
    int offset = -timeZone.timeZoneContainer.getTimeZone().getStandardOffset();
    
    String hourOffset = ((offset >= 0) ? "+" : "") + (offset / 60);
    String minuteOffset = padTwo(Math.abs(offset) % 60);

    return StringData.DAYS[jsdate.getDay()] + " "
        + StringData.MONTHS[jsdate.getMonth()] + " " + padTwo(jsdate.getDate())
        + " " + padTwo(jsdate.getHours()) + ":" + padTwo(jsdate.getMinutes())
        + ":" + padTwo(jsdate.getSeconds()) + " GMT" + hourOffset
        + minuteOffset + " " + jsdate.getFullYear();
  }

  /*
   * Some browsers have the following behavior:
   * 
   * // Assume a U.S. time zone with daylight savings
   * // Set a non-existent time: 2:00 am Sunday March 8, 2009
   * var date = new Date(2009, 2, 8, 2, 0, 0);
   * var hours = date.getHours(); // returns 1
   * 
   * The equivalent Java code will return 3. To compensate, we determine the
   * amount of daylight savings adjustment by comparing the time zone offsets
   * for the requested time and a time one day later, and add the adjustment to
   * the hours and minutes of the requested time.
   */

  /**
   * Detects if the requested time falls into a non-existent time range due to
   * local time advancing into daylight savings time. If so, push the requested
   * time forward out of the non-existent range.
   */
  private void fixDaylightSavings(int hours) {
    if ((jsdate.getHours() % 24) != (hours % 24)) {
      JsDate copy = JsDate.create(jsdate.getTime());
      copy.setDate(copy.getDate() + 1);
      int timeDiff = jsdate.getTimezoneOffset() - copy.getTimezoneOffset();

      // If the time zone offset is changing, advance the hours and
      // minutes from the initially requested time by the change amount
      if (timeDiff > 0) {
        int timeDiffHours = timeDiff / 60;
        int timeDiffMinutes = timeDiff % 60;
        int day = jsdate.getDate();
        int badHours = jsdate.getHours();
        if (badHours + timeDiffHours >= 24) {
          day++;
        }
        JsDate newTime = JsDate.create(jsdate.getFullYear(), jsdate.getMonth(),
            day, hours + timeDiffHours, jsdate.getMinutes() + timeDiffMinutes,
            jsdate.getSeconds(), jsdate.getMilliseconds());
        jsdate.setTime(newTime.getTime());
      }
    }
  }
  
  
  
	  @SuppressWarnings("deprecation")
	  private static Date createDate(int era, int year, int month, int date, int hours, int minutes, int seconds,
	                  int milliseconds, TimeZone timeZone)
	  {
	          int aYear = year - 1900;
	          if( era == 0)
	          {
	                  aYear = (year + 1899) * -1;
	          }
	
	          // This date is initially incorrect since it will be in browser time zone
	          // We shall mutate it to respect the given time zone
	          Date d = new Date(aYear, month, date, hours, minutes, seconds);
	          d.setTime(d.getTime() + milliseconds);
	
	
	          // Console.log("GWTDate createDate " + d);
	          int offsetConversion = d.getTimezoneOffset() - timeZone.timeZoneContainer.getTimeZone().getStandardOffset();
	          d.setMinutes(d.getMinutes() - offsetConversion);
	          d.setMinutes(d.getMinutes() - timeZone.timeZoneContainer.getTimeZone().getDaylightAdjustment(d));
	          return d;
	  }
  
}

